package com.example.xd.hot_fix_java

import android.content.Context
import android.os.Environment
import android.widget.Toast
import dalvik.system.DexClassLoader
import dalvik.system.PathClassLoader
import java.io.File
import java.lang.reflect.Array.getLength
import java.lang.reflect.Field
import java.util.*
import kotlin.collections.HashSet


/**
 * @auther XuDan on 2020/8/20.
 */
class FixDexUtil {
//    companion object {
//        val DEX_SUFFIX = ".dex"
//        val APK_SUFFIX = ".apk"
//        val JAR_SUFFIX = ".jar"
//        val ZIP_SUFFIX = ".zip"
//        val DEX_DIR = "odex"
//        val OPTIMIZE_DEX_DIR = "optimize_dex"
//        val loadedDex = HashSet<File>()
//    }
//
//    init {
//        loadedDex.clear()
//    }
//
//    /**
//     * 加载补丁，使用默认目录：data/data/包名/files/odex
//     *
//     * @param context
//     */
//    fun loadFixedDex(context: Context) {
//        loadFixedDex(context, null)
//    }
//
//    /**
//     * 加载补丁
//     *
//     * @param context       上下文
//     * @param patchFilesDir 补丁所在目录
//     */
//    fun loadFixedDex(
//        context: Context,
//        patchFilesDir: File?
//    ) {
//        // dex合并之前的dex
//        doDexInject(context, loadedDex)
//    }
//
//    /**
//     * @author bthvi
//     * @time 2018/6/25 0025 15:51
//     * @desc 验证是否需要热修复
//     */
//    fun isGoingToFix(context: Context): Boolean {
//        var canFix = false
//        val externalStorageDirectory =
//            Environment.getExternalStorageDirectory()
//
//        // 遍历所有的修复dex , 因为可能是多个dex修复包
//        val fileDir = if (externalStorageDirectory != null) File(
//            externalStorageDirectory,
//            "007"
//        ) else File(context.filesDir, DEX_DIR) // data/data/包名/files/odex（这个可以任意位置）
//        val listFiles = fileDir.listFiles()
//        if (listFiles != null) {
//            for (file in listFiles) {
//                if (file.name.startsWith("classes") &&
//                    (file.name.endsWith(DEX_SUFFIX)
//                            || file.name.endsWith(APK_SUFFIX)
//                            || file.name.endsWith(JAR_SUFFIX)
//                            || file.name.endsWith(ZIP_SUFFIX))
//                ) {
//                    loadedDex.add(file) // 存入集合
//                    //有目标dex文件, 需要修复
//                    canFix = true
//                }
//            }
//        }
//        return canFix
//    }
//
//    private fun doDexInject(
//        appContext: Context,
//        loadedDex: HashSet<File>
//    ) {
//        val optimizeDir = appContext.filesDir.absolutePath +
//                File.separator + OPTIMIZE_DEX_DIR
//        // data/data/包名/files/optimize_dex（这个必须是自己程序下的目录）
//        val fopt = File(optimizeDir)
//        if (!fopt.exists()) {
//            fopt.mkdirs()
//        }
//        try {
//            // 1.加载应用程序dex的Loader
//            val pathLoader = appContext.classLoader as PathClassLoader
//            for (dex in loadedDex) {
//                // 2.加载指定的修复的dex文件的Loader
//                val dexLoader = DexClassLoader(
//                    dex.absolutePath,  // 修复好的dex（补丁）所在目录
//                    fopt.absolutePath,  // 存放dex的解压目录（用于jar、zip、apk格式的补丁）
//                    null,  // 加载dex时需要的库
//                    pathLoader // 父类加载器
//                )
//                // 3.开始合并
//                // 合并的目标是Element[],重新赋值它的值即可
//                /**
//                 * BaseDexClassLoader中有 变量: DexPathList pathList
//                 * DexPathList中有 变量 Element[] dexElements
//                 * 依次反射即可
//                 */
//
//                //3.1 准备好pathList的引用
//                val dexPathList: Any = getPathList(dexLoader)
//                val pathPathList: Any = getPathList(pathLoader)
//                //3.2 从pathList中反射出element集合
//                val leftDexElements: Any = getDexElements(dexPathList)
//                val rightDexElements: Any = getDexElements(pathPathList)
//                //3.3 合并两个dex数组
//                val dexElements = combineArray(leftDexElements, rightDexElements)
//
//                // 重写给PathList里面的Element[] dexElements;赋值
//                val pathList: Any = getPathList(pathLoader) // 一定要重新获取，不要用pathPathList，会报错
//                setField(pathList, pathList.javaClass, "dexElements", dexElements)
//            }
//            Toast.makeText(appContext, "修复完成", Toast.LENGTH_SHORT).show()
//        } catch (e: Exception) {
//            e.printStackTrace()
//        }
//    }
//
//    /**
//     * 反射给对象中的属性重新赋值
//     */
//    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
//    private fun setField(
//        obj: Any,
//        cl: Class<*>,
//        field: String,
//        value: Any
//    ) {
//        val declaredField: Field = cl.getDeclaredField(field)
//        declaredField.setAccessible(true)
//        declaredField.set(obj, value)
//    }
//
//    /**
//     * 反射得到对象中的属性值
//     */
//    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
//    private fun getField(
//        obj: Any,
//        cl: Class<*>,
//        field: String
//    ): Any {
//        val localField = cl.getDeclaredField(field)
//        localField.isAccessible = true
//        return localField[obj]
//    }
//
//
//    /**
//     * 反射得到类加载器中的pathList对象
//     */
//    @Throws(
//        ClassNotFoundException::class,
//        NoSuchFieldException::class,
//        IllegalAccessException::class
//    )
//    private fun getPathList(baseDexClassLoader: Any): Any {
//        return getField(
//            baseDexClassLoader,
//            Class.forName("dalvik.system.BaseDexClassLoader"),
//            "pathList"
//        )
//    }
//
//    /**
//     * 反射得到pathList中的dexElements
//     */
//    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
//    private fun getDexElements(pathList: Any): Any {
//        return getField(pathList, pathList.javaClass, "dexElements")
//    }
//
//
//    /**
//     * 数组合并
//     */
//    private fun combineArray(arrayLhs: Any, arrayRhs: Any): Any? {
//        val clazz = arrayLhs.javaClass.componentType
//        val i: Int = Array.getLength(arrayLhs) // 得到左数组长度（补丁数组）
//        val j: Int = Array.getLength(arrayRhs) // 得到原dex数组长度
//        val k = i + j // 得到总数组长度（补丁数组+原dex数组）
//        val result: Any = Array.newInstance(clazz, k) // 创建一个类型为clazz，长度为k的新数组
//        System.arraycopy(arrayLhs, 0, result, 0, i)
//        System.arraycopy(arrayRhs, 0, result, i, j)
//        return result
//    }


}